function HTMLActuator() {
  this.tileContainer    = document.querySelector(".tile-container");
  this.scoreContainer   = document.querySelector(".score-container");
  this.bestContainer    = document.querySelector(".best-container");
  this.messageContainer = document.querySelector(".game-message");
  this.gameContainer    = document.querySelector(".game-container");

  this.score = 0;
  this.scale = 1;
  this.lastDistance = null;
  this.isPinching = false;

  // 添加触摸事件监听器
  this.bindTouchEvents();
}

HTMLActuator.prototype.bindTouchEvents = function() {
  const container = this.gameContainer;
  const self = this;

  container.addEventListener('touchstart', function(e) {
    if (e.touches.length === 2) {
      self.isPinching = true;
      self.lastDistance = self.getDistance(e.touches);
    }
  });

  container.addEventListener('touchmove', function(e) {
    if (self.isPinching && e.touches.length === 2) {
      e.preventDefault();
      const currentDistance = self.getDistance(e.touches);
      const delta = currentDistance - self.lastDistance;
      self.scale += delta * 0.005;
      self.scale = Math.max(0.5, Math.min(self.scale, 2.0)); // 限制缩放范围
      container.style.transform = `scale(${self.scale})`;
      self.lastDistance = currentDistance;
    }
  });

  container.addEventListener('touchend', function(e) {
    if (e.touches.length < 2) {
      self.isPinching = false;
      self.lastDistance = null;
    }
  });
};

HTMLActuator.prototype.getDistance = function(touches) {
  const dx = touches[0].clientX - touches[1].clientX;
  const dy = touches[0].clientY - touches[1].clientY;
  return Math.sqrt(dx * dx + dy * dy);
}

HTMLActuator.prototype.actuate = function (grid, metadata) {
  var self = this;

  window.requestAnimationFrame(function () {
    self.clearContainer(self.tileContainer);

    grid.cells.forEach(function (column) {
      column.forEach(function (cell) {
        if (cell) {
          self.addTile(cell);
        }
      });
    });

    self.updateScore(metadata.score);
    self.updateBestScore(metadata.bestScore);
    self.setLevel(metadata.maxValue);
    self.updateBestLevel(metadata.bestLevel);

    if (metadata.terminated) {
      if (metadata.over) {
        self.message(false); // You lose
      } else if (metadata.won) {
        self.message(true); // You win!
      }
    }

  });
};

// Continues the game (both restart and keep playing)
HTMLActuator.prototype.continueGame = function () {
  this.clearMessage();
};

HTMLActuator.prototype.clearContainer = function (container) {
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
};

HTMLActuator.prototype.addTile = function (tile) {
  var self = this;

  var wrapper   = document.createElement("div");
  var inner     = document.createElement("div");
  var position  = tile.previousPosition || { x: tile.x, y: tile.y };
  var positionClass = this.positionClass(position);

  // We can't use classlist because it somehow glitches when replacing classes
  var classes = ["tile", "tile-" + tile.value, positionClass];

  if (tile.value > 2048) classes.push("tile-super");

  this.applyClasses(wrapper, classes);

  inner.classList.add("tile-inner");
  var img = document.createElement('img');
img.src = 'pic/' + tile.value + '.jpg';
img.style.width = '100%';
img.style.height = '100%';
inner.appendChild(img);

  if (tile.previousPosition) {
    // Make sure that the tile gets rendered in the previous position first
    window.requestAnimationFrame(function () {
      classes[2] = self.positionClass({ x: tile.x, y: tile.y });
      self.applyClasses(wrapper, classes); // Update the position
    });
  } else if (tile.mergedFrom) {
    classes.push("tile-merged");
    this.applyClasses(wrapper, classes);

    // Render the tiles that merged
    tile.mergedFrom.forEach(function (merged) {
      self.addTile(merged);
    });
  } else {
    classes.push("tile-new");
    this.applyClasses(wrapper, classes);
  }

  // Add the inner part of the tile to the wrapper
  wrapper.appendChild(inner);

  // Put the tile on the board
  this.tileContainer.appendChild(wrapper);
};

HTMLActuator.prototype.applyClasses = function (element, classes) {
  element.setAttribute("class", classes.join(" "));
};

HTMLActuator.prototype.normalizePosition = function (position) {
  return { x: position.x + 1, y: position.y + 1 };
};

HTMLActuator.prototype.positionClass = function (position) {
  position = this.normalizePosition(position);
  return "tile-position-" + position.x + "-" + position.y;
};

HTMLActuator.prototype.updateScore = function (score) {
  this.clearContainer(this.scoreContainer);

  var difference = score - this.score;
  this.score = score;

  this.scoreContainer.textContent = this.score;

  if (difference > 0) {
    var addition = document.createElement("div");
    addition.classList.add("score-addition");
    addition.textContent = "+" + difference;

    this.scoreContainer.appendChild(addition);
  }
};

HTMLActuator.prototype.setLevel = function (level) {
    var container = document.querySelector('.level-container');
    if (container) {
      // 等级映射表
      const levelMap = {
        2: '柜面经理',
        4: '大堂经理',
        8: '内勤行长',
        16: '网点主任',
        32: '支行副行长',
        64: '支行行长',
        128: '分行副行长',
        256: '分行行长',
        512: '总行部门副总',
        1024: '总行总经理'
      };
      const levelText = levelMap[level] || level;
      container.textContent = levelText;
    }
  };
  HTMLActuator.prototype.updateBestScore = function (bestScore) {
  this.bestContainer.textContent = bestScore;
};

HTMLActuator.prototype.updateBestLevel = function (bestLevel) {
  this.bestLevelContainer = this.bestLevelContainer || document.querySelector('.best-level-container');
  // 等级映射表
  const levelMap = {
    2: '柜面经理',
    4: '大堂经理',
    8: '内勤行长',
    16: '网点主任',
    32: '支行副行长',
    64: '支行行长',
    128: '分行副行长',
    256: '分行行长',
    512: '总行部门副总',
    1024: '总行总经理',
    2048: '总行行长'
  };
  const bestLevelText = levelMap[bestLevel] || bestLevel;
  this.bestLevelContainer.textContent = bestLevelText;
};

HTMLActuator.prototype.message = function (won) {
  var type    = won ? "game-won" : "game-over";
  var message = won ? "You win!" : "Game over!";

  this.messageContainer.classList.add(type);
  this.messageContainer.getElementsByTagName("p")[0].textContent = message;
};

HTMLActuator.prototype.clearMessage = function () {
  // IE only takes one value to remove at a time.
  this.messageContainer.classList.remove("game-won");
  this.messageContainer.classList.remove("game-over");
};